#!/usr/local/bin/python
# Rob Dennis - Lurchington Q. Delacroix
# Keasby - TV renamer
# Project Homepage: http://code.google.com/p/keasby/

#--- Python Imports
import os
import logging
import shutil
from os.path import join as pjoin

#--- Keasby Imports
from keasby_base import KeasbyError, KeasbyTestCase

from keasby_toolbox import log

class KeasbyEmceeTestCase(KeasbyTestCase):
    """
    Unittest for KeasbyEmcee
    """

    def setUp(self):

        from keasby_emcee import KeasbyEmcee

        self.mc = KeasbyEmcee()

        self.sanity = self.mc


class KeasbyInterpreterTestCase(KeasbyTestCase):
    """
    Unittest for KeasbyInterpreter
    """
    def setUp(self):

        from keasby_interpreter import KeasbyInterpreter

        self.i = KeasbyInterpreter()
        self.sanity = self.i

    def prime_test(self, *value_list, **mapped_keys):

        from tv_episode import TvEpisodeFactory, TvEpisode

        key_names = ("seriesname", "seasonnumber", "episodenumber", "path")


##        if log.level <= logging.DEBUG:
##            for tv_ep_values in value_list:
##                log.debug(zip(key_names, tv_ep_values))
##                log.debug(dict(zip(key_names, tv_ep_values)))
##                log.debug(TvEpisode(**dict(zip(key_names, tv_ep_values))).to_dict())


        values = [dict(zip(key_names, tv_ep_values))
                                            for tv_ep_values in value_list]

        self.test_expected = TvEpisodeFactory.new(*values, **mapped_keys)

        self.test_media_path = self.test_expected.path

    def conduct_test(self, regex):
        """
        requires the test to be accurately primed
        """

        interpreted_answer = self.i.interpret(self.test_media_path, regex)

        self.assertLog(self.test_expected == interpreted_answer,
                        fail_message="\nexpected: %(expected)s\n!=\n"+
                                        "actual %(answer)s",
                        fail_message_kwargs=dict(
                                    expected=str(self.test_expected.to_dict()),
                                    answer=str(interpreted_answer.to_dict()))
                        )

        log.debug("conducting additional test for empty values")
        _expected = self.test_expected.to_dict()
        _answer = interpreted_answer.to_dict()

        if not isinstance(_expected, list):
            _expected = [_expected]

        if not isinstance(_answer, list):
            _answer = [_answer]

        for _test_dict, _expect_dict in zip(_answer, _expected):
            for key, value in _test_dict.iteritems():
                if key is "extra":
                    log.debug('not checking "extra" information')
                    continue

                self.assertLog(value, "Empty value: answer[%(key)s]=%(aval)s"+
                                        " expected[%(key)s]=%(xval)s",
                                dict(key=key, aval=value,
                                        xval=_expect_dict[key]))



    def test_normal_case(self):
        """
        Normal, well-formatted case
        """

        normal = (u"Scrubs", 1, 1,
                    pjoin(u"Scrubs", u"Season 01", u"Scrubs-S01E01.avi"))
        self.prime_test(normal)

        regex_str = [r"(?P<seriesname>.+?)",
                        r"Season 0*(?P<seasonnumber>\d+?)",
                        r".*-S\d+E0*(?P<episodenumber>\d+)"]

        normal_regex = str("\\"+(os.sep)).join(regex_str)

        self.conduct_test(normal_regex)

    def test_multi_ep_case(self):
        from keasby_options import kopt

        multi_ep1 = (u"Avatar The Last Airbender", 1, 1,
                        pjoin(u"Avatar The Last Airbender", u"Season 1",
                                u"Avatar s01e01-02.avi"))

        multi_ep2 = (u"Avatar The Last Airbender", 1, 2,
                        pjoin(u"Avatar The Last Airbender", u"Season 1",
                                u"Avatar s01e01-02.avi"))

        self.prime_test(multi_ep1, multi_ep2)

        regex_str = [r"(?P<seriesname>.+)",
                        r"Season (?P<seasonnumber>\d+)",
                        r".*?s\d+e(?P<episodenumber>"+
                            r"(?:\d+%s?)*)"%kopt.cl_opts.split_episode_char]

        multi_regex = str("\\"+(os.sep)).join(regex_str)

        self.conduct_test(multi_regex)

    def test_multi_ep_and_season_case(self):
        from keasby_options import kopt

        multi_ep1 = ("Avatar The Last Airbender", 1, 13,
                        pjoin("Avatar The Last Airbender", "Season 1-2",
                                "Avatar s01-02e13-01.avi"))

        multi_ep2 = ("Avatar The Last Airbender", 2, 1,
                        pjoin("Avatar The Last Airbender", "Season 1-2",
                                "Avatar s01-02e13-01.avi"))

        self.prime_test(multi_ep1, multi_ep2)

        regex_str = [r"(?P<seriesname>.+)",
                        r"Season (?P<seasonnumber>%s)"%
                            r"(?:\d+%s?)*"%kopt.cl_opts.split_episode_char,
                        r".*?s.+?e(?P<episodenumber>"+
                            r"(?:\d+%s?)*)"%kopt.cl_opts.split_episode_char]

        multi_regex = str("\\"+(os.sep)).join(regex_str)

        self.conduct_test(multi_regex)


    def test_interpret_string(self):
        """
        ensures that interpret method works with appropriate input
        """

        from keasby_interpreter import KeasbyInterpreterError

        self.assertTrue(self.i.interpret(""))
        self.assertTrue(self.i.interpret(u""))
        self.failUnlessRaises(KeasbyInterpreterError,
                                self.i.interpret,
                                None)


class KeasbyRepoTestCase(KeasbyTestCase):
    """
    Unittest for KeasbyRepo
    """

    def setUp(self):

        from keasby_repo import KeasbyRepo

        self.repo = KeasbyRepo()
        self.sanity = self.repo

class KeasbyConfigParserTestCase(KeasbyTestCase):
    """
    Unittest for KeasbyOptions
    """

    def setUp(self):

        from keasby_config_parser import KeasbyConfigParser

        self.cparser = KeasbyConfigParser()
        self.cparser.read("preferences.cfg")

        self.sanity = self.cparser

    def test_yield(self):

        for yielded_key, yielded_value in self.cparser.yield_dict_items():
            self.assertLog((yielded_key, yielded_value),
            pass_message = "for %(key)s, found %(value)s of %(type)s",
            pass_message_kwargs = {"key" : yielded_key,
                                    "value" : yielded_value,
                                    "type" : type(yielded_value)}
                                    )


class KeasbyOptionsTestCase(KeasbyTestCase):
    """
    Unittest for KeasbyOptions
    """

    def setUp(self):

        from keasby_options import KeasbyOptions

        self.options = KeasbyOptions()
        self.sanity = self.options


class KeasbyCacheTestCase(KeasbyTestCase):
    """
    Unittest for KeasbyCache
    """

    test_path = pjoin("keasby_unittest_cache", "test")
    test_file = pjoin(test_path, "test_file")
    test_zipfile = pjoin(test_path, "test.zip")
    test_zipped_file = "zip.txt"
    test_text = "what\nthe\nheck"

    def setUp(self):
        import zipfile
        #with syntax for zip files
        from contextlib import closing
        from keasby_cache import KeasbyCache

        self.cache = KeasbyCache()
        self.sanity = self.cache

        if not os.path.exists(self.cache.abspath(self.test_path)):
            os.makedirs(self.cache.abspath(self.test_path))

        #write testable file
        with open(self.cache.abspath(self.test_file), mode="wb") as test:
            test.write(self.test_text)

        #write testable zip file
        with open(self.cache.abspath(self.test_zipped_file), mode="wb") as tmp:
            tmp.write(self.test_text)

        with closing(zipfile.ZipFile(self.cache.abspath(self.test_zipfile),
                                       mode="w")) as archive:
            archive.write(self.cache.abspath(self.test_zipped_file),
                            self.test_zipped_file)

        #ensure it's there
        assert os.path.exists(self.cache.abspath(self.test_file))
        assert self.test_file in self.cache
        assert os.path.exists(self.cache.abspath(self.test_zipfile))
        assert self.test_zipfile in self.cache


        log.debug("test file: %s exists", self.test_file)
        log.debug("test file: %s exists", self.test_zipfile)

    def tearDown(self):
        shutil.rmtree(self.cache.path, True)

    def test_read(self):

        with self.cache.read(self.test_file) as fileobj:
            self.assertTrue(isinstance(fileobj, file),
                            "not a file: %s"%type(fileobj))

            self.assertEqual(self.test_text, fileobj.read())

    def test_write(self):

        new_path = pjoin(self.test_path, "write_test_path")
        new_file = pjoin(new_path, "test_file")
        new_text = "what\nthe\heck\write test"

        with self.cache.write(new_file) as writeobj:
            writeobj.write(new_text)


        self.assertTrue(os.path.exists(self.cache.abspath(new_file)),
                        "%s does not exist"%self.cache.abspath(new_file))
        self.assertTrue(new_file in self.cache)

        with self.cache.open(new_file) as readobj:
           self.assertEqual(new_text, readobj.read())

    def test_zip_read(self):
        #with syntax for zip files
        from contextlib import closing

        with closing(self.cache.read_zip(self.test_zipfile,
                                 self.test_zipped_file)) as fileobj:

            self.assertTrue(hasattr(fileobj, "read"),
                            "no read() method for: %s"%type(fileobj))
            self.assertEqual(self.test_text, fileobj.read())

        log.debug("checking assumption of only 1 file")

        with closing(self.cache.read_zip(self.test_zipfile)) as fileobj:
            self.assertTrue(hasattr(fileobj, "read"),
                            "no read() method for: %s"%type(fileobj))
            self.assertEqual(self.test_text, fileobj.read())

    def test_dump(self):

        self.assertEqual(self.cache.dump(self.test_file),
                         self.test_text)



class TvSeriesTestCase(KeasbyTestCase):
    """
    Unittest for TvSeries
    """

    def setUp(self):

        from tv_episode import TvSeries

        self.tvseries = TvSeries()
        self.sanity = self.tvseries

class TvMultiEpisodeTestCase(KeasbyTestCase):
    """
    Unittest for TvMultiEpisode
    """

    def setUp(self):

        from tv_episode import TvMultiEpisode

        self.tvmultiepisode = TvMultiEpisode()
        self.sanity = self.tvmultiepisode

class TvEpisodeTestCase(KeasbyTestCase):
    """
    Unittest for TvSeries
    """

    def setUp(self):

        from tv_episode import TvEpisode

        self.tvepisode = TvEpisode()
        self.sanity = self.tvepisode

class KeasbyRepoTestCase(KeasbyTestCase):
    """
    Unittest for TvSeries
    """

    def setUp(self):

        from keasby_repo import KeasbyRepo
        from tv_episode import TvEpisode, TvSeries

        self.test_tvep = TvEpisode(seriesname=u"Lost",
                                    seasonnumber=1,
                                    episodenumber=1)
        self.repo = KeasbyRepo()
        self.sanity = self.repo


if __name__ == "__main__":

    import unittest

    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(KeasbyEmceeTestCase))
    suite.addTest(unittest.makeSuite(KeasbyInterpreterTestCase))
    suite.addTest(unittest.makeSuite(KeasbyRepoTestCase))
    suite.addTest(unittest.makeSuite(KeasbyConfigParserTestCase))
    suite.addTest(unittest.makeSuite(KeasbyOptionsTestCase))
    suite.addTest(unittest.makeSuite(KeasbyCacheTestCase))
    suite.addTest(unittest.makeSuite(TvSeriesTestCase))
    suite.addTest(unittest.makeSuite(TvEpisodeTestCase))
    suite.addTest(unittest.makeSuite(KeasbyRepoTestCase))

    unittest.TextTestRunner(verbosity=2).run(suite)